﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nage.Algorithms.Solution;
using Nage.Algorithms.Population;
using Nage.Algorithms.Evolution;
using Nage.Algorithms.Stop;
using Utils.Logger;

namespace Nage.Algorithms
{
    /// <summary>
    /// Genetic algorithm class. Provides a scheme of 
    /// for genetic algorithm computations.
    /// </summary>
    /// <typeparam name="TSol">Type of a solution</typeparam>
    public class GeneticAlgorithm<TSol> : GeneticAlgorithmBase<TSol>, IGeneticAlgorithm<TSol> where TSol : ISolution
    {
        #region Events
        /// <summary>
        /// Fired when each generation step has been completed
        /// </summary>
        public event Action<IPopulation<TSol>> StepCompleted;
        #endregion

        #region Protected fields
        /// <summary>
        /// Current population
        /// </summary>
        protected IPopulation<TSol> currentPopulation;

        /// <summary>
        /// List of steps
        /// </summary>
        protected List<IPopulation<TSol>> steps;
        #endregion

        #region GetStepList
        /// <summary>
        /// When RegisterEachStep flag is on and Solve method has been invoked 
        /// returns a complete list of populations in each step
        /// </summary>
        /// <returns></returns>
        public virtual List<IPopulation<TSol>> GetStepList()
        {
            return steps;
        }
        #endregion

        #region Solve
        /// <summary>
        /// Starts to compute the genetic algorithm
        /// </summary>
        /// <returns>Final population</returns>
        public virtual IPopulation<TSol> Solve()
        {
            Initialize();
            CLogger.LogDebug("GeneticAlgorithm -> Solve | Creating population");
            currentPopulation = PopulationFactory.CreatePopulation();
            this.Step();

            while (!StopCondition.IsFulfilled())
            {
                CLogger.LogDebug("GeneticAlgorithm -> Solve | Evolving current population");
                currentPopulation = Evolution.Evolve(currentPopulation);
                this.Step();
            }

            CLogger.LogDebug("GeneticAlgorithm -> Solve | Done");
            return currentPopulation;
        }
        #endregion

        #region Initialize
        /// <summary>
        /// Checks initial parameters
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
            if (this.RegisterEachStep)
            {
                steps = new List<IPopulation<TSol>>();
            }
        }
        #endregion

        #region Step
        /// <summary>
        /// Performs step by setting current population solutions the common evaluator
        /// saving population and firing StepCompleted event
        /// </summary>
        protected void Step()
        {
            /* register evaluator for each solution */
            currentPopulation.SetAllEvaluations(SolutionEvaluator);

            /* save current population */
            if (RegisterEachStep)
            {
                steps.Add(currentPopulation);
            }

            /* raise step completed */
            if (StepCompleted != null)
            {
                StepCompleted(currentPopulation);
            }
        }
        #endregion
    }
}
